package transaction

import (
	"ChainCodeProject/tools"
	"ChainCodeProject/wallet"
	"bytes"
	"encoding/gob"
	"time"
)

/**
 * @author: 清秋
 * @email: 15770670622
 * @datetime: 10:29 2022/4/20
 * @Description:
 */

//创建一个交易结构体
type Transaction struct {
	//交易的唯一标识
	TXHash []byte
	//多个交易输出
	Output []Output
	//多个交易输入
	Input []Input
	//时间戳
	TimeStamp int64
}

//创建一个普通交易
func NewTransaction(from, to string, amount uint, spandableOutputs []UTXO) (*Transaction, error) {

	//c.构建input
	allinputs := make([]Input, 0)
	for _, utxo := range spandableOutputs {
		//遍历的是from要给的钱
		//不光需要公钥，因为签名需要私钥进行，所以需要把私钥存起来
		input := Input{
			Txid: utxo.TxId,
			Vout: utxo.Index,
			Sig:  nil,
			Pubk: nil,
		}
		allinputs = append(allinputs, input)
	}
	//2.构建Output
	var spendAmount uint
	allOutputs := make([]Output, 0)
	for _, out := range spandableOutputs {
		spendAmount += out.Value
		if spendAmount <= amount {
			// 遍历的是to收到的钱
			// 余额刚好够本次交易，不需要找零
			hash, err := wallet.GetPubHash(to)
			if err != nil {
				return nil, err
			}
			output := Output{
				Value:        out.Value,
				ScriptPubKey: hash,
			}
			allOutputs = append(allOutputs, output)
		} else {
			//发起交易会产生找零的情况
			spendAmount -= out.Value
			//还要再凑的金额
			neddAmount := amount - spendAmount

			hash, err := wallet.GetPubHash(to)
			if err != nil {
				return nil, err
			}

			output := Output{
				Value:        neddAmount,
				ScriptPubKey: hash,
			}
			allOutputs = append(allOutputs, output)

			pubhash, err := wallet.GetPubHash(from)
			if err != nil {
				return nil, err
			}
			backChange := Output{
				Value:        out.Value - neddAmount,
				ScriptPubKey: pubhash,
			}
			allOutputs = append(allOutputs, backChange)
		}
	}
	//3.给TXHash赋值
	//4.交易创建成功
	tx := Transaction{
		Output: allOutputs,
		Input:  allinputs,
	}
	tx.TimeStamp = time.Now().Unix()
	err, txBytes := tx.Serialize()
	if err != nil {
		return nil, err
	}
	hash := tools.GetHash(txBytes)
	tx.TXHash = hash
	return &tx, err
}

//创建一个CoinBase交易
func NewCoinBase(address string) (error, *Transaction) {
	pubHash, err := wallet.GetPubHash(address)
	if err != nil {
		return err, nil
	}
	coinbase := Transaction{
		Input: nil,
		Output: []Output{
			{
				Value:        50,
				ScriptPubKey: pubHash,
			},
		},
	}
	coinbase.TimeStamp = time.Now().Unix()
	//计算cb的hash值
	err, txBytes := coinbase.Serialize()
	if err != nil {
		return err, nil
	}
	hash := tools.GetHash(txBytes)
	coinbase.TXHash = hash
	return nil, &coinbase
}

//交易序列化
func (tx *Transaction) Serialize() (error, []byte) {
	var result bytes.Buffer
	en := gob.NewEncoder(&result)
	err := en.Encode(tx)
	if err != nil {
		return err, nil
	}
	return nil, result.Bytes()
}
